<?php
// @formatter:off
/**
 * @file WCGHolder.abs.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-04-30 02:04:27 +0000 (Tue, 30 Apr 2013) $
 *
 * $Id: WCGHolder.abs.php 63 2013-04-30 02:04:27Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0-BETA1/includes/holders/WCGHolder.abs.php $
 */
// @formatter:on

/**
 * @class WCGHolder
 *
 * This is the specification for a generic holder. It holds the logic to manage
 * list of object, preferable WCGTableRepresentation objects indexed by name and
 * id.
 *
 * @warning Every object called item should two basic properties:
 *  - name
 *  - id
 */
abstract class WCGHolder extends WCHolder {
	/**
	 * This is the main list where every element is finaly held.
	 * This list is organized by two main concepts:
	 * - First, every item will be a sub-list associated with a group id.
	 * - Second, every sub-list will contain two children:
	 *  -
	 *
	 * array(
	 * 	[group-id] => array(
	 * 		[byid] => array(
	 * 			[id] => ItemClass
	 * 			...
	 * 		)
	 * 		[byname] => array(
	 * 			[id] => ItemClass
	 * 			...
	 * 		)
	 * 	)
	 * 	...
	 * )
	 * @var array
	 */
	//protected	$_items	= array();
	/**
	 * @var array
	 */
	protected $_fullLoadedGroups = array();

	/**
	 * Constructor is protected because all holders must be singletons.
	 * Therefore, each subclass have to implemente a way to create and call
	 * the sole instance. For example, using something like this:
	 * @code
	 * protected static $_Instance;
	 *
	 * public static function Instance() {
	 * 	if(!isset(self::$_Instance)) {
	 * 		$c = __CLASS__;
	 * 		self::$_Instance = new $c();
	 * 	}
	 *
	 * 	return self::$_Instance;
	 * }
	 * @endcode
	 */
	protected function __construct() {
		global $wcDatabaseAccess;

		$this->_db = WCConnection::Instance();
		$this->_dbprefix = $wcDatabaseAccess['prefix'];
	}
	public function __destruct() {
		unset($this->_items);
		unset($_fullLoadedGroups);
	}
	/**
	 * Prevent users to clone the instance.
	 */
	public function __clone() {
		trigger_error(__CLASS__.": Clone is not allowed.", E_USER_ERROR);
	}
	//
	// Public methods
	public function create($name, WCGroup &$group) {
		$out = null;

		if($this->_PropertiesDupNames || !$this->findItemByName($name, $group)) {
			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__."[{$this->_PropertiesTable}]";
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "insert\n";
				$query .= "        into {$this->_dbprefix}$this->_PropertiesTable(\n";
				$query .= "                {$this->_PropertiesPrefix}group, {$this->_PropertiesPrefix}name)\n";
				$query .= "        values (:group, :name)";

				$stmt = $this->_db->prepare($stmtId, $query);
			}

			$params = array(
				":group" => $group->id,
				":name" => $name
			);

			if($stmt->execute($params)) {
				$out = $this->item($this->_db->lastInsertId(), $group);
			}
		}

		return $out;
	}
	public function item($id, WCGroup $group) {
		if($this->findItem($id, $group)) {
			return $this->_items[$group->id]["byid"][$id];
		} else {
			return false;
		}
	}
	public function itemByName($name, WCGroup &$group) {
		if($this->findItemByName($name, $group)) {
			return $this->_items[$group->id]["byname"][$name];
		} else {
			return false;
		}
	}
	public function itemIds(WCGroup &$group) {
		$out = array();

		if(!in_array($group->id, $this->_fullLoadedGroups)) {
			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__."[{$this->_PropertiesTable}]";
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "select  {$this->_PropertiesPrefix}id as id\n";
				$query .= "from    {$this->_dbprefix}{$this->_PropertiesTable}\n";
				$query .= "where   {$this->_PropertiesPrefix}group = :group\n";
				$query .= "order by {$this->_PropertiesPrefix}name asc\n";
				$stmt = $this->_db->prepare($stmtId, $query);
			}

			$params = array(":group" => $group->id);

			if($stmt->execute($params)) {
				foreach($stmt->fetchAll() as $row) {
					$out[] = $row["id"];
				}
			}
		} else {
			$out = array_keys($this->items($group));
		}

		return $out;
	}
	// 	public function items() {
	// 		trigger_error("This method is not available in for this class");
	// 	}
	public function items(WCGroup &$group) {
		if(!in_array($group->id, $this->_fullLoadedGroups)) {
			if(!isset($this->_items[$group->id])) {
				$this->_items[$group->id] = array(
					"byid" => array(),
					"byname" => array()
				);
			}

			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__."[{$this->_PropertiesTable}]";
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "select  {$this->_PropertiesPrefix}id as id\n";
				$query .= "from    {$this->_dbprefix}{$this->_PropertiesTable}\n";
				$query .= "where   {$this->_PropertiesPrefix}group = :group\n";
				$stmt = $this->_db->prepare($stmtId, $query);
			}

			$params = array(":group" => $group->id);

			if($stmt->execute($params)) {
				foreach($stmt->fetchAll() as $row) {
					$this->findItem($row["id"], $group);
				}

				$this->_fullLoadedGroups[] = $group->id;
			}
		}

		return in_array($group->id, $this->_fullLoadedGroups) ? $this->_items[$group->id]["byid"] : array();
	}
	public function resetCache() {
		parent::resetCache();

		$this->_fullLoadedGroups = array();
	}
	//
	// Protected Methods
	protected function addToCache(&$item) {
		$gid = $item->group + 0;
		if(!isset($this->_items[$gid])) {
			$this->_items[$gid] = array(
				"byid" => array(),
				"byname" => array()
			);
		}
		$this->_items[$gid]["byid"][$item->id] = $item;
		$this->_items[$gid]["byname"][$item->name] = $item;
	}
	protected function findItem($id, WCGroup &$group) {
		$out = false;

		if(!isset($this->_items[$group->id]) || !isset($this->_items[$group->id]["byid"][$id])) {
			$item = new $this->_PropertiesItemClass();
			@$item->load($id, $group);
			if($item->ok()) {
				$this->addToCache($item);
				$out = true;
			} else {
				unset($item);
			}
		} else {
			$out = true;
		}

		return $out;
	}
	protected function findItemByName($name, WCGroup &$group) {
		$out = false;

		if(!isset($this->_items[$group->id]) || !isset($this->_items[$group->id]["byname"][$name])) {
			/**
			 * @var WCGTableRepresentation
			 */
			$item = new $this->_PropertiesItemClass();
			@$item->loadByName($name, $group);
			if($item->ok()) {
				$this->addToCache($item);
				$out = true;
			} else {
				unset($item);
			}
		} else {
			$out = true;
		}

		return $out;
	}
	//
	// Public Class Methods
}
?>